#include "stdafx.h"

#include "scene/plane.h"
#include "util/check_glerror.h"

namespace natrium {
	namespace scene {
		Plane::Plane(float sizeX, float sizeZ, unsigned int numDivisionsX, unsigned int numDivisionsZ) {
			mNumElements = numDivisionsX * numDivisionsZ;
			assert(mNumElements > 0);
		
			size_t numEdges = (numDivisionsX + 1) * (numDivisionsZ + 1);

			std::vector<float> vertices(3 * numEdges); //xyz per edge
			std::vector<float> texCoords(2 * numEdges); //uv per edge
			std::vector<unsigned int> indices(6 * numDivisionsX * numDivisionsZ); //two triangles per square in the grid

			//center the grid around (0, 0, 0)
			float halfX = 0.5f * sizeX;
			float halfZ = 0.5f * sizeZ;

			float stepX = sizeX / static_cast<float>(numDivisionsX);
			float stepZ = sizeZ / static_cast<float>(numDivisionsZ);

			float texStepX = 1.0f / numDivisionsX;
			float texStepZ = 1.0f / numDivisionsZ;

			float x;
			float z;
			unsigned int vertexRef = 0;
			unsigned int texRef = 0;

			//first, generate vertices and texture coordinates at each vertex
			for (size_t i = 0; i <= numDivisionsZ; ++i) {
				z = stepZ * i - halfZ;

				for (size_t j = 0; j <= numDivisionsX; ++j) {
					x = stepX * j - halfX;

					vertices[vertexRef + 0] = x;
					vertices[vertexRef + 1] = 0.0f;
					vertices[vertexRef + 2] = z;
					vertexRef += 3;

					texCoords[texRef + 0] = j * texStepX;
					texCoords[texRef + 1] = i * texStepZ;
					texRef += 2;
				}
			}

			//now, create quads
			unsigned int row;
			unsigned int nextRow;
			unsigned int idxRef = 0;

			for (size_t i = 0; i < numDivisionsZ; ++i) {
				row = i * (numDivisionsX + 1);
				nextRow = (i + 1) * (numDivisionsX + 1);

				for (size_t j = 0; j < numDivisionsX; ++j) {
					indices[idxRef + 0] = row + j;
					indices[idxRef + 1] = nextRow + j;
					indices[idxRef + 2] = nextRow + j + 1;

					indices[idxRef + 3] = row + j;
					indices[idxRef + 4] = nextRow + j + 1;
					indices[idxRef + 5] = row + j + 1;

					idxRef += 6;
				}
			}

			//send the model to the graphics card
			glGenVertexArrays(1, &mHandle);
			glBindVertexArray(mHandle);

			unsigned int buffers[4];
			glGenBuffers(4, buffers);

			//bind the vertex buffer to location 0
			glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
			glBufferData(GL_ARRAY_BUFFER, 3 * numEdges * sizeof(float), vertices.data(), GL_STATIC_DRAW);
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
			glEnableVertexAttribArray(0);

			//normals are expected in location 1
			float normal[3] = { 0.0f, 1.0f, 0.0f };
			glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
			glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(float), normal, GL_STATIC_DRAW);
			glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
			glEnableVertexAttribArray(1);

			//texture coordinates in location 2
			glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
			glBufferData(GL_ARRAY_BUFFER, 2 * numEdges * sizeof(GLfloat), texCoords.data(), GL_STATIC_DRAW);
			glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
			glEnableVertexAttribArray(2);

			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[3]);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * numDivisionsX * numDivisionsZ * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);

			//we're done here, cleanup
			glBindVertexArray(0);
		}

		void Plane::render() const {
			glBindVertexArray(mHandle);
			glDrawElements(GL_TRIANGLES, 6 * mNumElements, GL_UNSIGNED_INT, nullptr);
			util::CheckGLError();
		}
	}
}